package com.sap.sap_mangers.until;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 限制接口调用频率工具类
 *
 * @author ZhouKai
 * 2022/9/26 17:18
 */
@Component
public class LimitCallFrequencyUntil {
    @Autowired
    public RedisCache redisCache;

    /**
     * 限制调用频率(一分钟) - 没到上限时redis对应数据+1,到了上限就返回false
     * <p>
     * 因为用的精确到分钟的时间做Hash的key,所以只支持一分钟级的统计
     *
     * @param redisHashKey redis中的Hash类型的key
     * @param maxCallNum   最大调用次数(每分钟)
     * @return 是否可以执行
     */
    public boolean limitCallFrequencyPerMinute(String redisHashKey, int maxCallNum) {
        //获取当前时间 格式"yyyy-MM-dd HH:mm" 精确到分钟
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String data = sdf.format(new Date());
        //限制接口调用次数共用逻辑
        if (!limitCallNum(redisHashKey, maxCallNum, data)) {
            //已到上限就不允许调用
            return false;
        }
        //设置redisHashKey缓存一分钟
        redisCache.expire(redisHashKey, 1, TimeUnit.MINUTES);
        //可以执行接口
        return true;
    }

    /**
     * 限制调用频率(一小时) - 没到上限时redis对应数据+1,到了上限就返回false
     * <p>
     * 因为用的精确到小时的时间做Hash的key,所以只支持一小时级的统计
     *
     * @param redisHashKey redis中的Hash类型的key
     * @param maxCallNum   最大调用次数(每小时)
     * @return 是否可以执行
     */
    public boolean limitCallFrequencyPerHour(String redisHashKey, int maxCallNum) {
        //获取当前时间 格式"yyyy-MM-dd HH" 精确到小时
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH");
        Date date = new Date();
        String data = sdf.format(date);
        if (!limitCallNum(redisHashKey, maxCallNum, data)) {
            //已到上限就不允许调用
            return false;
        }
        //缓存一小时
        redisCache.expire(redisHashKey, 1, TimeUnit.HOURS);
        return true;
    }

    /**
     * 限制调用频率(一天) - 没到上限时redis对应数据+1,到了上限就返回false
     * <p>
     * 因为用的精确到天的时间做Hash的key,所以只支持一天级的统计
     *
     * @param redisHashKey redis中的Hash类型的key
     * @param maxCallNum   最大调用次数(每天)
     * @return 是否可以执行
     */
    public boolean limitCallFrequencyPerDay(String redisHashKey, int maxCallNum) {
        //获取当前时间 格式"yyyy-MM-dd" 精确到小时
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        String data = sdf.format(date);
        if (!limitCallNum(redisHashKey, maxCallNum, data)) {
            //已到上限就不允许调用
            return false;
        }
        //缓存一天
        redisCache.expire(redisHashKey, 1, TimeUnit.DAYS);
        return true;
    }

    /**
     * 限制接口调用次数共用逻辑
     *
     * @param redisHashKey redis中Hash类型的key
     * @param maxCallNum   限制最大次数
     * @param data         时间(不同方法调用时精度不同，限制每分钟使用次数精度就只到分钟)
     * @return 是否能执行 (如果能够执行记得根据时间精度设置缓存时间)
     */
    private boolean limitCallNum(String redisHashKey, int maxCallNum, String data) {
        //获取redis
        Map<String, Object> cacheMap = redisCache.getCacheMap(redisHashKey);
        if (cacheMap == null) {
            //如果当前redis中没有redisHashKey，就加入本时间段的数据(redis有数据key就会存在)
            redisCache.setCacheMapValue(redisHashKey, data, 1);
        } else {
            //如果能找到redisHashKey，查询当前时间段调用过的次数
            int callNumber = (int) cacheMap.getOrDefault(data, 0);
            //如果调用次数已到上限，不允许执行接口
            if (callNumber >= maxCallNum) {
                return false;
            }
            //否则将调用次数+1后加入Hash
            redisCache.setCacheMapValue(redisHashKey, data, callNumber + 1);
        }
        return true;
    }
}
